package it.uniroma2.art.owlart.protegeimpl.factory;

import it.uniroma2.art.owlart.exceptions.ModelCreationException;
import it.uniroma2.art.owlart.exceptions.ModelUpdateException;
import it.uniroma2.art.owlart.exceptions.VocabularyInitializationException;
import it.uniroma2.art.owlart.models.BaseRDFTripleModel;
import it.uniroma2.art.owlart.models.ModelFactory;
import it.uniroma2.art.owlart.models.OWLModel;
import it.uniroma2.art.owlart.models.RDFModel;
import it.uniroma2.art.owlart.models.RDFSModel;
import it.uniroma2.art.owlart.models.SKOSModel;
import it.uniroma2.art.owlart.models.SKOSXLModel;
import it.uniroma2.art.owlart.models.TripleQueryModelHTTPConnection;
import it.uniroma2.art.owlart.models.UnloadableModelConfigurationException;
import it.uniroma2.art.owlart.models.UnsupportedModelConfigurationException;
import it.uniroma2.art.owlart.models.impl.OWLModelImpl;
import it.uniroma2.art.owlart.models.impl.RDFModelImpl;
import it.uniroma2.art.owlart.models.impl.RDFSModelImpl;
import it.uniroma2.art.owlart.models.impl.SKOSModelImpl;
import it.uniroma2.art.owlart.models.impl.SKOSXLModelImpl;
import it.uniroma2.art.owlart.protegeimpl.models.BaseRDFModelProtegeImpl;
import it.uniroma2.art.owlart.protegeimpl.models.OWLModelProtegeImpl;
import it.uniroma2.art.owlart.protegeimpl.models.conf.ProtegeModelConfiguration;
import it.uniroma2.art.owlart.vocabulary.OWL;
import it.uniroma2.art.owlart.vocabulary.RDF;
import it.uniroma2.art.owlart.vocabulary.RDFS;

import java.util.ArrayList;
import java.util.Collection;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import edu.stanford.smi.protege.model.Project;
import edu.stanford.smi.protegex.owl.database.OWLDatabaseKnowledgeBaseFactory;
import edu.stanford.smi.protegex.owl.jena.JenaKnowledgeBaseFactory;

public class ARTModelFactoryProtegeImpl implements ModelFactory<ProtegeModelConfiguration> {

	protected static Logger logger = LoggerFactory.getLogger(ARTModelFactoryProtegeImpl.class);

	protected boolean populatingW3CVocabularies = false;

	ArrayList<Class<? extends ProtegeModelConfiguration>> supportedConfigurationClasses;

	public ARTModelFactoryProtegeImpl() {
		supportedConfigurationClasses = new ArrayList<Class<? extends ProtegeModelConfiguration>>();
		supportedConfigurationClasses.add(ProtegeModelConfiguration.class);
	}

	public BaseRDFModelProtegeImpl loadRDFBaseModel(String baseuri, String repositoryDirectory,
			ProtegeModelConfiguration conf) throws ModelCreationException {
		return loadRDFBaseModel(baseuri, repositoryDirectory, "test.owl");
	}

	public BaseRDFModelProtegeImpl loadRDFBaseModel(String baseuri, String repositoryDirectory, String file)
			throws ModelCreationException {
		logger.info("creating File based Protege implementation of RDFBaseModel at location "
				+ repositoryDirectory + "/" + file);
		JenaKnowledgeBaseFactory factory = new JenaKnowledgeBaseFactory();
		Collection<Object> errors = new ArrayList<Object>();
		Project project = Project.createNewProject(factory, errors);
		String workingDir = System.getProperty("user.dir").replace("\\", "/");
		JenaKnowledgeBaseFactory.setOWLFileName(project.getSources(), "file:/" + workingDir + "/"
				+ repositoryDirectory + "/" + file);
		// JenaKnowledgeBaseFactory.setOWLFileName(project.getSources(), repositoryDirectory + "/test.owl");
		project.createDomainKnowledgeBase(factory, errors, true);
		BaseRDFModelProtegeImpl rep = new BaseRDFModelProtegeImpl(project);
		logger.info("loaded Protege ART RDFBaseModel model: " + rep);
		return rep;
	}

	public BaseRDFModelProtegeImpl loadRDFBaseModel(String dbDriver, String dbUrl, String dbTableName,
			String dbUserName, String dbPassword) throws ModelCreationException {
		logger.info("creating Database Protege OWLModel: " + dbUrl);
		OWLDatabaseKnowledgeBaseFactory factory = new OWLDatabaseKnowledgeBaseFactory();
		Collection<Object> errors = new ArrayList<Object>();
		Project project = Project.createNewProject(factory, errors);
		OWLDatabaseKnowledgeBaseFactory.setSources(project.getSources(), dbDriver, dbUrl, dbTableName,
				dbUserName, dbPassword);
		project.createDomainKnowledgeBase(factory, errors, true);
		BaseRDFModelProtegeImpl rep = new BaseRDFModelProtegeImpl(project);
		return rep;
	}

	public void clearModel(BaseRDFTripleModel rep) throws ModelUpdateException {
		rep.clearRDF();
	}

	public RDFModel loadRDFModel(String baseuri, String repositoryDirectory, ProtegeModelConfiguration conf)
			throws ModelCreationException {
		BaseRDFModelProtegeImpl baseModel = loadRDFBaseModel(baseuri, repositoryDirectory, conf);
		RDFModelImpl model = new RDFModelImpl(baseModel);
		return model;
	}

	public RDFSModel loadRDFSModel(String baseuri, String repositoryDirectory, ProtegeModelConfiguration conf)
			throws ModelCreationException {
		BaseRDFModelProtegeImpl baseModel = loadRDFBaseModel(baseuri, repositoryDirectory, conf);
		RDFSModelImpl model = new RDFSModelImpl(baseModel);
		return model;
	}

	public OWLModel loadOWLModel(String baseuri, String repositoryDirectory, ProtegeModelConfiguration conf)
			throws ModelCreationException {
		BaseRDFModelProtegeImpl baseModel = loadRDFBaseModel(baseuri, repositoryDirectory, conf);
		OWLModelImpl model = new OWLModelProtegeImpl(baseModel);
		return model;
	}

	/*
	 * TODO ACTUALLY I SHOULD OVERRIDE SKOSMODELIMPL with a single overridden method, which is: getOWLModel,
	 * since this should return am OWLModelProtegeImpl and not the default OWLModelImpl which superclasses
	 * SKOSModelImpl
	 * 
	 * (non-Javadoc)
	 * 
	 * @see it.uniroma2.art.owlart.models.ModelFactory#loadSKOSModel(java.lang.String, java.lang.String,
	 * it.uniroma2.art.owlart.models.conf.ModelConfiguration)
	 */
	public SKOSModel loadSKOSModel(String baseuri, String repositoryDirectory, ProtegeModelConfiguration conf)
			throws ModelCreationException {
		BaseRDFModelProtegeImpl baseModel = loadRDFBaseModel(baseuri, repositoryDirectory, conf);
		SKOSModelImpl model = new SKOSModelImpl(baseModel);
		return model;
	}

	/*
	 * TODO
	 * 
	 * ACTUALLY I SHOULD OVERRIDE SKOSXLMODELIMPL with a single overridden method, which is: getOWLModel,
	 * since this should return am OWLModelProtegeImpl and not the default OWLModelImpl which superclasses
	 * SKOSXLModelImpl
	 * 
	 * (non-Javadoc)
	 * 
	 * @see it.uniroma2.art.owlart.models.ModelFactory#loadSKOSXLModel(java.lang.String, java.lang.String,
	 * it.uniroma2.art.owlart.models.conf.ModelConfiguration)
	 */
	public SKOSXLModel loadSKOSXLModel(String baseuri, String repositoryDirectory,
			ProtegeModelConfiguration conf) throws ModelCreationException {
		BaseRDFModelProtegeImpl baseModel = loadRDFBaseModel(baseuri, repositoryDirectory, conf);
		SKOSXLModelImpl model = new SKOSXLModelImpl(baseModel);
		return model;
	}

	public OWLModel loadOWLModel(String baseuri, String repositoryDirectory, String file)
			throws ModelCreationException, ModelUpdateException {
		BaseRDFModelProtegeImpl baseModel = loadRDFBaseModel(baseuri, repositoryDirectory, file);
		OWLModelImpl model = new OWLModelProtegeImpl(baseModel);
		try {
			RDF.Res.initialize(model);
			RDFS.Res.initialize(model);
			OWL.Res.initialize(model);
			model.setBaseURI(baseuri);
			return model;
		} catch (VocabularyInitializationException e) {
			throw new ModelCreationException(e);
		}
	}

	public OWLModel loadOWLModel(String dbDriver, String dbUrl, String dbTableName, String dbUserName,
			String dbPassword) throws ModelCreationException {
		BaseRDFModelProtegeImpl baseModel = loadRDFBaseModel(dbDriver, dbUrl, dbTableName, dbUserName,
				dbPassword);
		OWLModelImpl model = new OWLModelProtegeImpl(baseModel);
		try {
			RDF.Res.initialize(model);
			RDFS.Res.initialize(model);
			OWL.Res.initialize(model);
			// model.setBaseURI(baseuri);
			return model;
		} catch (VocabularyInitializationException e) {
			throw new ModelCreationException(e);
		}
	}

	public void closeModel(BaseRDFTripleModel rep) throws ModelUpdateException {
		rep.close();
	}

	public <MCImpl extends ProtegeModelConfiguration> MCImpl createModelConfigurationObject(
			Class<MCImpl> mcclass) throws UnsupportedModelConfigurationException,
			UnloadableModelConfigurationException {
		logger.debug("creating ModelConfigurationObject");
		if (supportedConfigurationClasses.contains(mcclass)) {
			try {
				logger.debug("requested model configuration class: " + mcclass);
				MCImpl mConf = (MCImpl) mcclass.newInstance();
				return mConf;
			} catch (InstantiationException e) {
				throw new UnloadableModelConfigurationException(mcclass);
			} catch (IllegalAccessException e) {
				throw new UnloadableModelConfigurationException(mcclass);
			}
		} else
			throw new UnsupportedModelConfigurationException(this, mcclass);

	}

	public Collection<Class<? extends ProtegeModelConfiguration>> getModelConfigurations() {
		return supportedConfigurationClasses;
	}

	public TripleQueryModelHTTPConnection loadTripleQueryHTTPConnection(String endpointURL)
			throws ModelCreationException {
		// TODO Auto-generated method stub
		return null;
	}

	public void setPopulatingW3CVocabularies(boolean pref) {
		populatingW3CVocabularies = pref;
	}

	public boolean isPopulatingW3CVocabularies() {
		return populatingW3CVocabularies;
	}

}
